using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Microsoft.Practices.WizardFramework;
using Microsoft.Practices.RepositoryFactory.CustomPages.UI;
using Microsoft.Practices.RepositoryFactory.CustomPages.Helpers;
using SD = Microsoft.Practices.RepositoryFactory.SchemaDiscovery.ObjectModel.Base;
using System.ComponentModel.Design;
using System.Collections;
using System.Configuration;
using Microsoft.Practices.RepositoryFactory.CodeGeneration.ObjectModel;
using Microsoft.Practices.RepositoryFactory.CodeGeneration.ObjectModel.Mappings.Enums;
using Microsoft.Practices.RecipeFramework.Extensions.CommonHelpers;
using System.Windows.Forms.Design;

namespace Microsoft.Practices.RepositoryFactory.CustomPages
{
    /// <summary/>
    public partial class EntitiesSelectorCP : CustomWizardPage
    {
        #region Fields

        TreeView tables;
        private ArrayList entities;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="T:EntitiesSelectorCP"/> class.
        /// </summary>
        /// <param name="parent">The parent.</param>
        public EntitiesSelectorCP(WizardForm parent)
            : base(parent)
        {
            InitializeComponent();
        }

        #endregion

        #region Arguments

        /// <summary>
        /// Gets or sets the entities.
        /// </summary>
        /// <value>The entities.</value>
        [RecipeArgument]
        public ArrayList Entities
        {
            get
            {
                return this.entities;
            }
            set
            {
                this.entities = value;
            }
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Called when [activated].
        /// </summary>
        public override void OnActivated()
        {
            IDictionaryService dictservice = (IDictionaryService)GetService(typeof(IDictionaryService));

            if(dictservice.GetValue("Entities") == null)
            {
                trvEntities.Nodes.Clear();
            }

            GetTablesTreeView();
            LoadSelectedNodes();
            propBrowser.SelectedObject = null;
            SaveArgument();
            base.OnActivated();
        }

        private void propBrowser_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            RefreshNodes();
        }

        private void trvEntities_MouseLeave(object sender, EventArgs e)
        {
            SaveArgument();
        }

        private void trvEntities_Leave(object sender, EventArgs e)
        {
            SaveArgument();
        }

        private void trvEntities_AfterSelect(object sender, TreeViewEventArgs e)
        {
            RefreshProperties(e.Node.Tag);
        }

        private void trvEntities_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            if (string.IsNullOrEmpty(e.Label))
            {
                e.CancelEdit = true;
                return;
            }

            if (e.Node is EntityNode)
            {
                try
                {
                    ((Entity)e.Node.Tag).Name = e.Node.Text = CodeDomHelper.ConvertToValidIdentifier(e.Label);
                }
                catch (ArgumentException ex)
                {
                    IUIService uiService = (IUIService)this.GetService(typeof(IUIService));
                    uiService.ShowError(ex);
                }
                RefreshProperties(e.Node.Tag);
            }
            else if (e.Node is PropertyNode)
            {
                try
                {
                    ((Property)e.Node.Tag).Name = e.Node.Text = CodeDomHelper.ConvertToValidIdentifier(e.Label);
                }
                catch (ArgumentException ex)
                {
                    IUIService uiService = (IUIService)this.GetService(typeof(IUIService));
                    uiService.ShowError(ex);
                }
                RefreshProperties(e.Node.Tag);
            }
            e.CancelEdit = true;
        }

        private void trvEntities_KeyUp(object sender, KeyEventArgs e)
        {
            if(e.KeyCode == Keys.F2)
            {
                trvEntities.LabelEdit = true;
                trvEntities.SelectedNode.BeginEdit();
            }
        }

        #endregion

        #region Private Implementation
        
        private void GetTablesTreeView()
        {
            IDictionaryService dictservice = (IDictionaryService)GetService(typeof(IDictionaryService));
            this.tables = (TreeView)dictservice.GetValue("TablesTreeView");
        }

        private ArrayList GetSelectedEntities()
        {
            ArrayList selectedEntities = new ArrayList();

            foreach(TreeNode node in this.tables.Nodes[0].Nodes)
            {
                if(string.Compare(node.Text, "Tables",  StringComparison.InvariantCultureIgnoreCase) == 0 ||
                   string.Compare(node.Text, "Views", StringComparison.InvariantCultureIgnoreCase) == 0)
                {
                    foreach(TreeNode objectNode in node.Nodes)
                    {
                        if(objectNode.Checked)
                        {
                            Entity entity;

                            if (string.Compare(node.Text, "Tables", StringComparison.InvariantCultureIgnoreCase) == 0)
                            {
                                entity = CreateEntity<SD.Table>(objectNode);
                            }
                            else
                            {
                                entity = CreateEntity<SD.View>(objectNode);
                            }

                            selectedEntities.Add(entity);
                        }
                    }
                }
            }

            return selectedEntities;
        }

        private static Entity CreateEntity<T>(TreeNode node) where T : SD.Table
        {
            const string DBO_SCHEMA = "dbo";

            Entity entity = new Entity();
            T tableTemp = (T)node.Tag;

            if(string.Compare(tableTemp.Schema, DBO_SCHEMA, StringComparison.InvariantCultureIgnoreCase) == 0)
            {
                entity.Name = CodeDomHelper.GetValidIdentifier(tableTemp.Name);
            }
            else
            {
                entity.Name = CodeDomHelper.GetValidIdentifier(tableTemp.Schema + tableTemp.Name);
            }

            entity.TableName = tableTemp.FullName;

            foreach(ColumnNode columnNode in node.Nodes)
            {
                if(columnNode.Checked)
                {
                    SD.Column columnTemp = (SD.Column)columnNode.Tag;

                    Property property = new Property();
                    property.Name = CodeDomHelper.GetValidIdentifier(columnTemp.Name);
                    property.ColumnName = columnTemp.Name;
                    property.Type = columnTemp.DbDataType.Type;
                    property.IsNullable = columnTemp.IsNullable;
                    property.IsPrimaryKey = columnTemp.IsPrimaryKey;

                    entity.Properties.Add(property);
                }
            }
            return entity;
        }

        private void LoadSelectedNodes()
        {
            if(trvEntities.Nodes.Count > 0)
            {
                foreach(TreeNode node in this.tables.Nodes[0].Nodes)
                {
                    if (string.Compare(node.Text, "Tables", StringComparison.InvariantCultureIgnoreCase) == 0)
                    {
                        SyncEntities<SD.Table>(node);
                    }
                    else if (string.Compare(node.Text, "Views", StringComparison.InvariantCultureIgnoreCase) == 0)
                    {
                        SyncEntities<SD.View>(node);
                    }
                }
            }
            else
            {
                //First Time
                ArrayList selectedEntities = GetSelectedEntities();
                FolderNode parentNode = new FolderNode("Entities");

                foreach(Entity entity in selectedEntities)
                {
                    EntityNode node = CreateEntityNode(entity);
                    parentNode.Nodes.Add(node);
                }

                trvEntities.Nodes.Add(parentNode);
            }

            trvEntities.Sort();
        }

        private void SyncEntities<T>(TreeNode node) where T : SD.Table
        {
            foreach(TreeNode objectNode in node.Nodes)
            {
                T tableTemp = (T)objectNode.Tag;

                bool founded = false;

                foreach(EntityNode entityNode in trvEntities.Nodes[0].Nodes)
                {
                    Entity entity = (Entity)entityNode.Tag;

                    if(string.Compare(entity.TableName, tableTemp.FullName, StringComparison.InvariantCultureIgnoreCase) == 0)
                    {
                        if(objectNode.Checked)
                        {
                            foreach(ColumnNode columnNode in objectNode.Nodes)
                            {
                                SD.Column columnTemp = (SD.Column)columnNode.Tag;

                                bool founded1 = false;

                                foreach(PropertyNode propertyNode in entityNode.Nodes)
                                {
                                    Property property = (Property)propertyNode.Tag;

                                    if(string.Compare(property.ColumnName, columnTemp.Name, StringComparison.InvariantCultureIgnoreCase) == 0)
                                    {
                                        if(!columnNode.Checked)
                                        {
                                            propertyNode.Remove();
                                            entity.Properties.Remove(property);
                                        }

                                        founded1 = true;
                                        break;
                                    }
                                }

                                if(!founded1)
                                {
                                    if(columnNode.Checked)
                                    {
                                        Property property = CreateProperty((SD.Column)columnNode.Tag);
                                        entityNode.Nodes.Add(new PropertyNode(property));
                                        entity.Properties.Add(property);
                                    }
                                }
                            }
                        }
                        else
                        {
                            entityNode.Remove();
                        }
                        founded = true;
                        break;
                    }
                }

                if(!founded)
                {
                    if(objectNode.Checked)
                    {
                        Entity entity = CreateEntity<SD.Table>(objectNode);
                        EntityNode entityNode = CreateEntityNode(entity);
                        trvEntities.Nodes[0].Nodes.Add(entityNode);
                    }
                }
            }
        }

        private Property CreateProperty(SD.Column column)
        {
            Property property = new Property();
            property.Name = column.Name;
            property.ColumnName = column.Name;
            property.Type = column.DbDataType.Type;
            property.IsNullable = column.IsNullable;
            property.IsPrimaryKey = column.IsPrimaryKey;

            return property;
        }

        private static EntityNode CreateEntityNode(Entity entity)
        {
            EntityNode node = new EntityNode(entity);

            foreach(Property prop in entity.Properties)
            {
                node.Nodes.Add(new PropertyNode(prop));
            }
            return node;
        }

        private void RefreshNodes()
        {
            foreach(EntityNode entityNode in trvEntities.Nodes[0].Nodes)
            {
                entityNode.Text = ((Entity)entityNode.Tag).Name;

                foreach(PropertyNode propertyNode in entityNode.Nodes)
                {
                    Property property = (Property)propertyNode.Tag;
                    propertyNode.Text = property.Name;
                }
            }

            trvEntities.Sort();
        }

        private void RefreshProperties(object target)
        {
            propBrowser.SelectedObject = target;
        }

        private ArrayList GetEntities()
        {
            ArrayList entities = new ArrayList();

            foreach(EntityNode entityNode in trvEntities.Nodes[0].Nodes)
            {
                Entity entity = (Entity)entityNode.Tag;
                entities.Add(entity);
            }

            return entities;
        }

        private void SaveArgument()
        {
            entities = GetEntities();
            IDictionaryService dictservice = (IDictionaryService)GetService(typeof(IDictionaryService));
            dictservice.SetValue("Entities", entities);
        }

        #endregion
    }
}
